package service

import (
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/util/guid"
	"github.com/mojocn/base64Captcha"
)

var Captcha = captchaService{}

type captchaService struct{}

var (
	// 默认内容存储设置
	captchaStore  = base64Captcha.DefaultMemStore
	captchaDriver = newDriver()
)

// 创建验证码引擎字符
func newDriver() *base64Captcha.DriverString {
	driver := &base64Captcha.DriverString{
		Height: 44,
		Width:  126,
		// 噪声计数
		NoiseCount: 5,
		// 验证码线条
		ShowLineOptions: base64Captcha.OptionShowHollowLine | base64Captcha.OptionShowSlimeLine | base64Captcha.OptionShowSineLine,
		Length:          4,            // 生成的随机字符串长度
		Source:          "1234567890", // 用于生成随机字符串的原始字符
		//BgColor:         nil,	// 背景颜色
		Fonts: []string{"wqy-microhei.ttc"}, // 字体家族，如果第一个不存在，则取第二个，直到取到为止
	}
	return driver.ConvertFonts()
}

// 创建验证码，直接输出验证码图片内容到响应体中
func (s *captchaService) NewAndStore(r *ghttp.Request, key string) error {
	// 创建验证码实例
	c := base64Captcha.NewCaptcha(captchaDriver, captchaStore)
	// 生成ID，验证码内容，和答案(这里不使用默认生成的ID，而用我们自定义生成的32位全局唯一ID)
	// (content和answer均为字符串，两个字符串的值是一样的，content用于生成前端展示的图片验证码，answer用于后端验证前端输入的验证码是否正确)
	_, content, answer := c.Driver.GenerateIdQuestionAnswer()
	// 根据验证码内容生成二进制内容
	item, _ := c.Driver.DrawCaptcha(content)
	// 自定义生成32字节全局唯一ID，一共32位，每位占一个字节
	captchaStoreKey := guid.S()
	// 每个用户对应不同的session, 不同session中的相同key是不会相互影响的
	// 每一个验证码有一个id对应一个answer，事先将id到answer的映射关系存储起来，之后前端输入验证码(captcha)数字，
	// 再通过Verify方法对前端传入的captcha进行验证，大致逻辑是先通过id取之前存储的answer，然后比较answer与captcha是否一致，一致则返回true，
	// 否则返回false
	// 但是如何保证取出每个用户所申请的验证码id能，例如用户A申请了一个验证码，其id为A1，用户B申请了一个验证码，其id为B1,等到用户A在前端输入
	// 验证码提交后，如何找到用户A之前申请的验证码？势必需要在申请的时候将用户A的信息与其所申请的验证码id映射关系存储起来
	// 所以这里先通过用户Session将每个用户申请的验证码id存储到该用户Session中，之后再通过该用户的Session取出id即可
	err := r.Session.Set(key, captchaStoreKey)
	if err != nil {
		return err
	}
	c.Store.Set(captchaStoreKey, answer)
	// 将二进制内容写入响应中
	_, err = item.WriteTo(r.Response.Writer)
	return err
}

// 校验验证码
func (s *captchaService) VerifyAndClear(r *ghttp.Request, key string, captcha string) bool {
	defer r.Session.Remove(key)
	// 根据key获取captcha的全局唯一ID
	captchaStoreKey := r.Session.GetString(key)
	// 第三个参数为true表示每次校验完后都会清除之前存储的验证码id到answer的映射关系
	return captchaStore.Verify(captchaStoreKey, captcha, true)
}
